Spring笔记(十五)—— Spring JDBC 访问数据库之 SimpleJdbc

使用 SimpleJdbcInsert 插入数据

从具有最少配置选项的 SimpleJdbcInsert 类开始,在数据访问层的初始化方法中实例化 SimpleJdbcInsert。对于下面的示例,初始化方法是 setDataSource 方法,不需要继承 SimpleJdbcInsert 类,只需创建一个新实例并使用 withTableName 方法设置表名。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class JdbcActorDao implements ActorDao {
private JdbcTemplate jdbcTemplate;
private SimpleJdbcInsert insertActor;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
this.insertActor = new SimpleJdbcInsert(dataSource).withTableName("t_actor");
}
public void add(Actor actor) {
Map<String, Object> parameters = new HashMap<String, Object>(3);
parameters.put("id", actor.getId());
parameters.put("first_name", actor.getFirstName());
parameters.put("last_name", actor.getLastName());
insertActor.execute(parameters);
}
// ... additional methods
}

这里使用的 execute 方法将一个简单的 java.utils.Map 作为其唯一的参数,需要注意的是,用于 Map 的键必须匹配数据库中定义的表的列名,因为我们需要读取元数据以构造实际的 insert 语句。

使用 SimpleJdbcInsert 检索自动生成的键

此示例使用与前面相同的插入,但不是传递 id,而是检索自动生成的键并将其设置在新的 Actor 对象上。创建 SimpleJdbcInsert 时,除了指定表名称之外,还可以使用 usingGeneratedKeyColumns 方法指定生成的键列的名称。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class JdbcActorDao implements ActorDao {
private JdbcTemplate jdbcTemplate;
private SimpleJdbcInsert insertActor;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
this.insertActor = new SimpleJdbcInsert(dataSource)
.withTableName("t_actor")
.usingGeneratedKeyColumns("id");
}
public void add(Actor actor) {
Map<String, Object> parameters = new HashMap<String, Object>(2);
parameters.put("first_name", actor.getFirstName());
parameters.put("last_name", actor.getLastName());
Number newId = insertActor.executeAndReturnKey(parameters);
actor.setId(newId.longValue());
}
// ... additional methods
}

使用上述方法执行插入操作时,不用将 ID 添加到 Map 中,而是调用 executeAndReturnKey 方法,这将返回一个 java.lang.Number 对象,我们可以使用它创建在域类中使用的数值类型的实例。如果有多个自动生成的列,或者生成的值是非数字的,那么可以使用从 executeAndReturnKeyHolder 方法返回的 KeyHolder。

为 SimpleJdbcInsert 指定列

使用 usingColumns 方法指定列:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class JdbcActorDao implements ActorDao {
private JdbcTemplate jdbcTemplate;
private SimpleJdbcInsert insertActor;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
this.insertActor = new SimpleJdbcInsert(dataSource)
.withTableName("t_actor")
.usingColumns("first_name", "last_name")
.usingGeneratedKeyColumns("id");
}
public void add(Actor actor) {
Map<String, Object> parameters = new HashMap<String, Object>(2);
parameters.put("first_name", actor.getFirstName());
parameters.put("last_name", actor.getLastName());
Number newId = insertActor.executeAndReturnKey(parameters);
actor.setId(newId.longValue());
}
// ... additional methods
}

使用 SqlParameterSource 提供参数值

使用 Map 提供参数值不是最方便使用的类,Spring 提供了两个 SqlParameterSource 接口的实现。第一个是 BeanPropertySqlParameterSource,它将使用相应的 getter 方法提取 JavaBean 中的参数值。下面是例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class JdbcActorDao implements ActorDao {
private JdbcTemplate jdbcTemplate;
private SimpleJdbcInsert insertActor;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
this.insertActor = new SimpleJdbcInsert(dataSource)
.withTableName("t_actor")
.usingGeneratedKeyColumns("id");
}
public void add(Actor actor) {
SqlParameterSource parameters = new BeanPropertySqlParameterSource(actor);
Number newId = insertActor.executeAndReturnKey(parameters);
actor.setId(newId.longValue());
}
// ... additional methods
}

另一个是 MapSqlParameterSource(类似于Map),但提供了一个更方便的可以链接的 addValue 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class JdbcActorDao implements ActorDao {
private JdbcTemplate jdbcTemplate;
private SimpleJdbcInsert insertActor;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
this.insertActor = new SimpleJdbcInsert(dataSource)
.withTableName("t_actor")
.usingGeneratedKeyColumns("id");
}
public void add(Actor actor) {
SqlParameterSource parameters = new MapSqlParameterSource()
.addValue("first_name", actor.getFirstName())
.addValue("last_name", actor.getLastName());
Number newId = insertActor.executeAndReturnKey(parameters);
actor.setId(newId.longValue());
}
// ... additional methods
}

使用 SimpleJdbcCall 调用存储过程

SimpleJdbcCall 类利用数据库中的元数据来查找输入和输出参数的名称,因此可以不必显式地声明它们。如果希望执行此操作,或者如果参数(如 ARRAY 或 STRUCT)没有自动映射到 Java 类,那么可以声明参数。第一个例子显示了一个简单的过程,它只从 MySQL 数据库返回 VARCHAR 和 DATE 格式的标量值。示例过程读取指定的 actor 条目,并以 out 参数的形式返回 first_name,last_name 和 birth_date 列。

1
2
3
4
5
6
7
8
9
10
CREATE PROCEDURE read_actor (
IN in_id INTEGER,
OUT out_first_name VARCHAR(100),
OUT out_last_name VARCHAR(100),
OUT out_birth_date DATE)
BEGIN
SELECT first_name, last_name, birth_date
INTO out_first_name, out_last_name, out_birth_date
FROM t_actor where id = in_id;
END;

in_id 参数包含需要查找的 actor 的 id,out 参数返回表中读取的数据。

下面是使用上述存储过程的 SimpleJdbcCall 配置的示例,除了 DataSource 之外,唯一的配置选项是存储过程的名称。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class JdbcActorDao implements ActorDao {
private JdbcTemplate jdbcTemplate;
private SimpleJdbcCall procReadActor;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
this.procReadActor = new SimpleJdbcCall(dataSource)
.withProcedureName("read_actor");
}
public Actor readActor(Long id) {
SqlParameterSource in = new MapSqlParameterSource()
.addValue("in_id", id);
Map out = procReadActor.execute(in);
Actor actor = new Actor();
actor.setId(id);
actor.setFirstName((String) out.get("out_first_name"));
actor.setLastName((String) out.get("out_last_name"));
actor.setBirthDate((Date) out.get("out_birth_date"));
return actor;
}
// ... additional methods
}

execute 方法接受 IN 参数,并返回一个 Map,该 Map 包含由存储过程中指定的名称键入的任何输出参数。在这种情况下,它们是 out_first_name,out_last_name 和 out_birth_date。

存储在结果映射中的 out 参数的名称与数据库中的 out 参数名称相同,其可以在数据库之间变化。为了使代码更便于移植,应该做一个不区分大小写的查找,或者指示 Spring 使用 LinkedCaseInsensitiveMap。后者需要创建自己的 JdbcTemplate 并将 setResultsMapCaseInsensitive 属性设置为 true,然后将此定制的 JdbcTemplate 实例传递到 SimpleJdbcCall 的构造函数中。以下是此配置的示例:

1
2
3
4
5
6
7
8
9
10
11
12
public class JdbcActorDao implements ActorDao {
private SimpleJdbcCall procReadActor;
public void setDataSource(DataSource dataSource) {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.setResultsMapCaseInsensitive(true);
this.procReadActor = new SimpleJdbcCall(jdbcTemplate)
.withProcedureName("read_actor");
}
// ... additional methods
}

显式声明要用于 SimpleJdbcCall 的参数

使用方法声明参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class JdbcActorDao implements ActorDao {
private SimpleJdbcCall procReadActor;
public void setDataSource(DataSource dataSource) {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.setResultsMapCaseInsensitive(true);
this.procReadActor = new SimpleJdbcCall(jdbcTemplate)
.withProcedureName("read_actor")
.withoutProcedureColumnMetaDataAccess()
.useInParameterNames("in_id")
.declareParameters(
new SqlParameter("in_id", Types.NUMERIC),
new SqlOutParameter("out_first_name", Types.VARCHAR),
new SqlOutParameter("out_last_name", Types.VARCHAR),
new SqlOutParameter("out_birth_date", Types.DATE)
);
}
// ... additional methods
}

使用 SimpleJdbcCall 调用存储过程函数

调用存储函数的方式与调用存储过程几乎相同,除了需要提供的是函数名而不是过程名。使用 withFunctionName 方法传入要调用函数,并生成函数调用的相应字符串。executeFunction 用于执行函数,并返回函数返回值作为指定类型的对象,这意味着不必从结果映射中检索返回值。以下示例基于一个名为 get_actor_name 的存储函数,它返回一个 actor 的全名。如下是这个函数的 MySQL 源码:

1
2
3
4
5
6
7
8
9
CREATE FUNCTION get_actor_name (in_id INTEGER)
RETURNS VARCHAR(200) READS SQL DATA
BEGIN
DECLARE out_name VARCHAR(200);
SELECT concat(first_name, ' ', last_name)
INTO out_name
FROM t_actor where id = in_id;
RETURN out_name;
END;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class JdbcActorDao implements ActorDao {
private JdbcTemplate jdbcTemplate;
private SimpleJdbcCall funcGetActorName;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.setResultsMapCaseInsensitive(true);
this.funcGetActorName = new SimpleJdbcCall(jdbcTemplate)
.withFunctionName("get_actor_name");
}
public String getActorName(Long id) {
SqlParameterSource in = new MapSqlParameterSource()
.addValue("in_id", id);
String name = funcGetActorName.executeFunction(String.class, in);
return name;
}
// ... additional methods
}

热评文章